﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web.Compilation;
using System.Xml.Linq;

namespace OF.Utility
{
    public static class TextResourceManager
    {
        #region Private Member

        private const string NODE_NAME = "TextResourcesPath";
        private static readonly string s_BaseFolderPath = GetBaseFolderPath();
        private static Dictionary<string, Assembly> s_ResDict = new Dictionary<string, Assembly>();
        private static object s_lockObject = new object();

        private static string GetBaseFolderPath()
        {
            string path = ConfigurationManager.AppSettings[NODE_NAME];
            if (path == null || path.Trim().Length <= 0)
            {
                return Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "Configuration/TextResources");
            }
            string p = Path.GetPathRoot(path);
            if (p == null || p.Trim().Length <= 0) // 说明是相对路径
            {
                path = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, path);
            }
            return path;
        }


        private static void LoadAllResources()
        {
            lock (s_lockObject)
            {
                if (s_ResDict.Count > 0) return;
                //var assemList = AppDomain.CurrentDomain.GetAssemblies();
                //var assemList = BuildManager.GetReferencedAssemblies();
                //foreach (Assembly assem in assemList)
                //{
                //    if (!assem.IsDynamic
                //        && !assem.GetName().Name.StartsWith("System.")
                //        && !assem.GetName().Name.StartsWith("Microsoft."))
                //    {
                //        var resNames = assem.GetManifestResourceNames();
                //        if (resNames != null && resNames.Length > 0)
                //        {
                //            foreach (var resName in resNames)
                //            {
                //                if (resName != null
                //                    && resName.EndsWith(".config")
                //                    && !s_ResDict.ContainsKey(resName))
                //                {
                //                    s_ResDict[resName] = assem;//fix exception:add duplicate key
                //                                               //s_ResDict.Add(resName, assem);
                //                }
                //            }
                //        }
                //    }
                //}

                var baseDirectory = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath ?? AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
                var assemPathList = Directory.GetFiles(baseDirectory, "OF.*.dll");

                foreach (var assemPath in assemPathList)
                {
                    var assem = Assembly.LoadFile(assemPath);
                    var resNames = assem.GetManifestResourceNames();

                    if (resNames != null && resNames.Length > 0)
                    {
                        foreach (var resName in resNames)
                        {
                            if (resName != null && resName.EndsWith(".config"))
                            {
                                s_ResDict[resName] = assem;
                            }
                        }
                    }
                }
            }
        }

        private static Dictionary<string, string> LoadFromResources(string resourceName)
        {
            if (s_ResDict.Count == 0)
            {
                LoadAllResources();
            }

            resourceName = resourceName.Replace(@"\", ".").ToLower();
            //把en-us,zh-cn变成en_us, zh_cn
            resourceName = Regex.Replace(resourceName, @"([a-zA-Z]{2})-([a-zA-Z]{2})(?=\.)", @"$1_$2");

            string foundResName = s_ResDict.Keys.FirstOrDefault(p => p != null && p.ToLower().IndexOf(resourceName) > -1);

            Assembly assem;
            if (foundResName != null && s_ResDict.TryGetValue(foundResName, out assem))
            {
                using (Stream stream = assem.GetManifestResourceStream(foundResName))
                {
                    if (stream != null)
                    {
                        XElement doc = XElement.Load(new StreamReader(stream));
                        return parseXml(doc);
                    }
                }
            }
            return new Dictionary<string, string>(0);
        }

        private static Dictionary<string, string> LoadTextResource(string filePath)
        {
            if (!File.Exists(filePath))
            {
                return new Dictionary<string, string>(0);
                //throw new FileNotFoundException("Can't find the CodeNamePair config file: " + getFullConfigPath + "!");
            }
            XElement doc = XElement.Load(filePath);
            return parseXml(doc);
        }

        private static Dictionary<string, string> parseXml(XElement doc)
        {
            Dictionary<string, string> rst = new Dictionary<string, string>();

            foreach (var x in doc.Descendants("Message"))
            {
                if (x.Attribute("name") == null || x.Attribute("name").Value == null || x.Attribute("name").Value.Trim().Length <= 0)
                {
                    throw new ApplicationException("There are some 'Message' node without attribute 'name' in the TextResource config file, please check it!");
                }
                string name = x.Attribute("name").Value.Trim().ToUpper();
                if (rst.ContainsKey(name))
                {
                    throw new ApplicationException("Duplicated value '" + x.Attribute("name").Value.Trim() + "' of attribute 'name' in 'Message' node in the TextResource config file, please check it (ex: ignore case)!");
                }
                rst.Add(name, EnvironmentVariableManager.ReplaceVariable(x.Value));
            }
            return rst;
        }

        /// <summary>
        /// 获取Message的描述信息
        /// </summary>
        /// <param name="resouceFileTitle">资源文件的名称（不包含语言和后缀名部分，但需要包含相对于Resources根目录的路径，使用‘.’分隔目录，不区分大小写）</param>
        /// <param name="keyName">Message的键值</param>
        /// <param name="inputLanguageCode">需要调用的Message语言版本</param>
        /// <param name="assemblyName">程序集名称，主要用于区分不同程序集出现同一资源文件。默认值为:null</param>
        /// <returns>Message 描述</returns>
        private static string GetText(string resouceFileTitle, string keyName, string inputLanguageCode, string assemblyName = null)
        {
            string path = resouceFileTitle.Trim().Trim('.').Replace('.', Path.DirectorySeparatorChar);
            var assemblyPath = string.Empty;
            if (!string.IsNullOrEmpty(assemblyName))
            {
                if (assemblyName.IndexOf(".dll") > -1)
                {
                    assemblyPath = assemblyName.Substring(0, assemblyName.IndexOf(".dll"));
                }
                assemblyPath += ".TextResources.";
            }
            string fileName = string.Format("{0}.config", path.Trim());

            string filePath = Path.Combine(s_BaseFolderPath, fileName);
            string key = string.Format("{0}{1}", assemblyPath, fileName.ToUpper());
            Dictionary<string, string> resDic = CacheManager.GetWithLocalCache<Dictionary<string, string>>(key,
                () =>
                {
                    var dict = LoadTextResource(filePath);
                    if (dict.Count == 0)
                    {
                        var newFileName = string.Format("{0}{1}", assemblyPath, fileName);
                        dict = LoadFromResources(newFileName);
                    }
                    return dict;
                });
            string txt;
            if (resDic == null
                || !resDic.TryGetValue(keyName.ToUpper(), out txt))
            {
                //如果找不到任何值，则返回传入的描述key值
                txt = keyName;
            }
            return txt;
        }

        internal static string HandleFirstChar(string text, FirstCharType firstCharType)
        {
            if (firstCharType != FirstCharType.None && !string.IsNullOrEmpty(text))
            {
                var firstChar = text[0];
                if (firstCharType == FirstCharType.Upper)
                {
                    firstChar = Char.ToUpper(firstChar);
                }
                else if (firstCharType == FirstCharType.Lower)
                {
                    firstChar = Char.ToLower(firstChar);
                }
                return firstChar + text.Substring(1);
            }
            return text;
        }

        #endregion

        /// <summary>
        /// 获取TextResource配置文件所在的根目录（该目录路径默认为应用程序根目录下的Configuration/TextResources，但可以由web.config或app.config的appsettings/add节点来配置：<add key="TextResourcesPath" value="" />）
        /// </summary>
        public static string BaseFolderPath
        {
            get { return s_BaseFolderPath; }
        }

        /// <summary>
        /// 获取TextResource中的描述信息，本方法将自动使用当前线程上下文的语言信息
        /// </summary>
        /// <param name="resouceFileTitle">资源文件的名称（不包含语言和后缀名部分，但需要包含相对于TextResource根目录的路径，使用‘.’分隔目录，不区分大小写）</param>
        /// <param name="keyName">对应到配置文件中Message节点的键值</param>
        /// <param name="assemblyName">程序集名称，主要用于区分不同程序集出现同一资源文件。默认值为:null</param>
        /// <param name="firstCharType">首字符大小写转换类型。默认值：FirstCharType.None</param>
        /// <returns>TextResource的Message描述</returns>
        public static string GetText(string resouceFileTitle, string keyName, string assemblyName = null, FirstCharType firstCharType = FirstCharType.None)
        {
            string inputLanguageCode = Thread.CurrentThread.CurrentCulture.Name.ToLower();
            var text = GetText(resouceFileTitle, keyName, inputLanguageCode, assemblyName);
            return HandleFirstChar(text, firstCharType);
        }
    }

    public static class LangHelper
    {
        public static string GetLanguageCode()
        {
            return System.Threading.Thread.CurrentThread.CurrentCulture.Name.ToLower();
        }
        /// <summary>
        /// 获取文字性内容
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static string GetText(string key)
        {
            return GetText(key, "text");
        }

        /// <summary>
        /// 获取文字性内容
        /// </summary>
        /// <param name="key">key</param>
        /// <param name="fileName">文件名</param>
        /// <param name="assemblyName">程序集名称，主要用于区分不同程序集出现同一资源文件。默认值为:null</param>
        /// <param name="firstCharType">首字符大小写转换类型。默认值：FirstCharType.None</param>
        /// <returns></returns>
        public static string GetText(string key, string fileName, string assemblyName = null, FirstCharType firstCharType = FirstCharType.None)
        {
            var languageCode = System.Threading.Thread.CurrentThread.CurrentCulture.Name.ToLower();
            string text = TextResourceManager.GetText(languageCode + "." + fileName, key, assemblyName, firstCharType);
            if (text == null)
            {
                return TextResourceManager.HandleFirstChar(key, firstCharType);
            }
            return text;
        }

        /// <summary>
        /// 获取图片
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static string GetImage(string key)
        {
            return GetText(key, "image");
        }

        /// <summary>
        /// 获取图片
        /// </summary>
        /// <param name="key"></param>
        /// <param name="fileName">配置文件名称</param>
        /// <returns></returns>
        public static string GetImage(string key, string fileName)
        {
            return GetText(key, fileName);
        }

        /// <summary>
        /// 是否中文环境
        /// </summary>
        /// <returns></returns>
        public static bool isChinese()
        {
            return GetLanguageCode() == "zh-cn";
        }
    }

    /// <summary>
    /// 多语言 首字符大小写转换类型
    /// </summary>
    public enum FirstCharType
    {
        /// <summary>
        /// 不处理
        /// </summary>
        None,
        /// <summary>
        /// 转为大写
        /// </summary>
        Upper,
        /// <summary>
        /// 转为小写
        /// </summary>
        Lower
    }
}
